﻿#pragma once

#include "scheduler/scheduler.hh"
#ifndef DISABLE_SB_CODE
#include "util/box_std_allocator.hh"
#endif
#include <memory>
#include <queue>
#include <unordered_map>

namespace kratos {
namespace service {

class ServiceBox;
class SchedulerImpl;

/// Timer type
enum class Type_ {
  NONE = 0,
  ONCE = 1, ///< 一次性定时器
  LOOP,     ///< 循环定时器
};

/// Timer callback
using SchedulerCallback = std::function<bool(std::uint64_t, std::time_t)>;

/// Timer node
struct ScheduleNode {
  std::time_t trigger_time{std::time(0)}; ///< 触发时间戳，毫秒
  Type_ type{Type_::NONE};                ///< 定时器类型
  std::time_t duration{std::time(0)};     ///< 持续时间，毫秒
  bool operator<(const ScheduleNode &node);
  std::uint64_t user_data{std::uint64_t(0)}; ///< 用户数据
  std::uint64_t id{std::uint64_t(0)};        ///<  定时器ID
  SchedulerCallback cb;                      ///< 定时器回调
  bool deleted{false};                       ///< 删除标记
  std::uint64_t co_id{0};                    ///< 协程ID
  SchedulerImpl *scheduler{nullptr};         ///< 定时器调度器
};

/**
 * 容器比较函数外壳.
 */
struct NodeShell {
  const NodeShell &operator=(const NodeShell &rht) = delete;
  std::shared_ptr<ScheduleNode> node_ptr;
  NodeShell(std::shared_ptr<ScheduleNode> ptr);
  NodeShell();
  NodeShell(const NodeShell &rht);
  NodeShell(NodeShell &&rht) noexcept;
  const NodeShell &operator=(NodeShell &&rht) noexcept;
  bool operator<(const NodeShell &rht) const noexcept;
  operator bool() noexcept;
};

// STRUCT TEMPLATE greater
struct HandlePtrGreater {
  bool operator()(const NodeShell &a, const NodeShell &b) const {
    return a.node_ptr->trigger_time > b.node_ptr->trigger_time;
  }
};

/**
 * 定时器调度器实现.
 */
class SchedulerImpl : public Scheduler {
#ifdef DISABLE_SB_CODE
  using TimerQueue =
      std::priority_queue<NodeShell, std::vector<NodeShell>, HandlePtrGreater>;
  using TimerMap =
      std::unordered_map<std::uint64_t, std::shared_ptr<ScheduleNode>>;
#else
  using TimerQueue = std::priority_queue<
      NodeShell, std::vector<NodeShell, kratos::service::Allocator<NodeShell>>,
      HandlePtrGreater>;
  using TimerMap =
      kratos::service::PoolUnorederedMap<std::uint64_t,
                                         std::shared_ptr<ScheduleNode>>;
  ServiceBox *box_{nullptr}; ///< 服务容器
#endif                        // DISABLE_SB_CODE
  TimerQueue queue_;          ///< Timer queue
  std::uint64_t timer_id_{1}; ///< 定时器ID池
  TimerMap timer_map_;        ///< 定时器表
  std::size_t co_count_{0};   ///< 协程定时器数量
  std::size_t noco_count_{0}; ///< 非协程定时器数量

public:
  SchedulerImpl(ServiceBox *box);
  virtual ~SchedulerImpl();
  virtual auto
  do_schedule(std::time_t ms,
              std::size_t max_count = std::numeric_limits<std::size_t>::max())
      -> std::size_t override;
  virtual auto schedule(std::time_t duration, SchedulerCallback cb,
                        std::uint64_t udata = 0) -> std::uint64_t override;
  virtual auto schedule_co(std::time_t duration, SchedulerCallback cb,
                           std::uint64_t udata = 0) -> std::uint64_t override;
  virtual auto schedule_many(std::time_t duration, SchedulerCallback cb,
                             std::uint64_t udata = 0) -> std::uint64_t override;
  virtual auto schedule_co_many(std::time_t duration, SchedulerCallback cb,
                                std::uint64_t udata = 0)
      -> std::uint64_t override;
  virtual auto cancel(std::uint64_t) -> bool override;
  virtual auto size() -> std::size_t override;
  virtual auto size_co() -> std::size_t override;
  virtual auto size_noco() -> std::size_t override;

private:
  /**
   * 获取一个超时的定时器.
   *
   * \param ms 当前时间戳
   * \return 超时的定时器
   */
  NodeShell pop(std::time_t ms);
  /**
   * 将定时器从定时器表删除.
   *
   * \param id 定时器ID
   */
  void delete_from_map(std::uint64_t id);
};

} // namespace service
} // namespace kratos
